<?php
/**
* 名称：文件上传类。
* @final     yes
* @package   tryLibrary.base
* @version   1.0
* @copyright (c) 2014/7, Habakkuk
*/
final class Uploader{
    /**
    * @const int PHP系统中除上传成功的其它错误总代称。
    */
    const UPLOAD_ERR=-999;

    /**
    * @const int 设置上传属性的值不正确。
    */
    const UPLOAD_ERR_CUSTOM_ATTRIBUTE=-8;

    /**
    * @const int 文件移动失败。
    */
    const UPLOAD_ERR_CUSTOM_MOVE=-7;

    /**
    * @const int 非法文件名。
    */
    const UPLOAD_ERR_CUSTOM_FILENAME=-6;

    /**
    * @const int 自动生成文件名函数不是函数类型。
    */
    const UPLOAD_ERR_CUSTOM_AUTONAMEFUNCTION=-5;

    /**
    * @const int 上传路径不存在。
    */
    const UPLOAD_ERR_CUSTOM_PATH=-4;

    /**
    * @const int 文件类型(扩展名)不符合设置要求。
    */
    const UPLOAD_ERR_CUSTOM_EXTENSION=-3;

    /**
    * @const int 文件体积不符合设置要求。
    */
    const UPLOAD_ERR_CUSTOM_SIZE=-2;

    /**
    * @const int 文件域不存在。
    */
    const UPLOAD_ERR_CUSTOM_NOFIELD=-1;

    /**
    * @const int=0 文件上传成功。
    */
    const UPLOAD_ERR_OK=UPLOAD_ERR_OK;

    /**
    * @const int=1 上传文件大小超出了php.ini中upload_max_filesize的值。
    */
    const UPLOAD_ERR_INI_SIZE=UPLOAD_ERR_INI_SIZE;

    /**
    * @const int=2 上传文件大小超出了表单中MAX_FILE_SIZE的值。
    */
    const UPLOAD_ERR_FORM_SIZE=UPLOAD_ERR_FORM_SIZE;

    /**
    * @const int=3 文件只上传了一部分，没有被全部上传。
    */
    const UPLOAD_ERR_PARTIAL=UPLOAD_ERR_PARTIAL;

    /**
    * @const int=4 文件域中没有文件。
    */
    const UPLOAD_ERR_NO_FILE=UPLOAD_ERR_NO_FILE;

    /**
    * @const int=6 找不到临时目录。
    */
    const UPLOAD_ERR_NO_TMP_DIR=UPLOAD_ERR_NO_TMP_DIR;

    /**
    * @const int=7 写入失败，可能没有权限。
    */
    const UPLOAD_ERR_CANT_WRITE=UPLOAD_ERR_CANT_WRITE;

    /**
    * @const int=8 文件上传过程中被停止。
    */
    const UPLOAD_ERR_EXTENSION=UPLOAD_ERR_EXTENSION;

    /**
    * @const string 预定义图像文件类型。
    */
    const UPLOAD_EXT_IMAGE='gif,jpg,png,bmp,jpeg';

    /**
    * @const string 预定义文档文件类型。
    */
    const UPLOAD_EXT_FILE='pdf,txt,doc,docx,xls,xlsx';

    /**
    * @const string 上传类型之一元单项。
    */
    const UPLOADTYPE_SINGLE='single';

    /**
    * @const string 上传类型之一元多项全部合格。
    */
    const UPLOADTYPE_MULTI_ALL='all';

    /**
    * @const string 上传类型之一元多项部分合格。
    */
    const UPLOADTYPE_MULTI_PART='part';

    /**
    * @const string 上传类型之多元全部合格。
    */
    const UPLOADTYPE_MULTIPLEX_ALL='multiplex_all';

    /**
    * @const string 上传类型之多元部分合格。
    */
    const UPLOADTYPE_MULTIPLEX_PART='multiplex_part';

    /**
    * 重构的文件域对象存储堆。
    * @var      array
    * @default array()
    * @access  private
    */
    private $Field=array();

    /**
    * 重构的文件域对象数量。
    * @var     int
    * @default 0
    * @access  private
    */
    private $FieldCount=0;

    /**
    * 上传成功的文件数量。
    * @var     int
    * @default 0
    * @access  private
    */
    private $UploadCount=0;

    /**
    * 存储上传后的文件名标量。
    * @var     string|array
    * @default array()
    * @access  private
    */
    private $Filename=array();

    /**
    * 存储上传过程中错误代码标量。
    * @var     int|array
    * @default NULL
    * @access  private
    */
    private $ErrorCode=NULL;

    /**
    * 存储上传过程中错误信息标量。
    * @var     string|array
    * @default NULL
    * @access  private
    */
    private $ErrorMessage=NULL;

    /**
    * 指示是不是第一次使用AddField方法。
    * @var     bool
    * @default TRUE
    * @access  private
    */
    private $IsFirstAddField=TRUE;

    /**
    * 检查一次上传设置。
    * @var      bool
    * @default FALSE
    * @access  private
    */
    private $IsCheckAttributeOnce=FALSE;

    /**
    * 检查上传设置时发现错误是否终止程式。
    * @var      bool
    * @default FALSE
    * @access  private
    */
    private $IsCheckAttributeErrorStop=FALSE;

    /**
    * 只检查一个文件域对象。
    * @var     bool
    * @default FALSE
    * @access  private
    */
    private $IsCheckFieldOnce=FALSE;

    /**
    * 检查文件域对象发现错误是否终止程式。
    * @var      bool
    * @default FALSE
    * @access  private
    */
    private $IsCheckFieldErrorStop=FALSE;

    /**
    * 上传文件类型，值是single或multi。
    * @var     string
    * @default NULL
    * @access  public
    */
    public $Type=NULL;

    /**
    * 指定上传文件要符合的大小。
    * @var      int
    * @default NULL
    * @access  public
    */
    public $Size=NULL;

    /**
    * 指定上传文件要我们要符合的扩展名。
    * @var     array
    * @default NULL
    * @access  public
    */
    public $Extension=NULL;

    /**
    * 文件上传的完整路径。
    * @var     string
    * @default NULL
    * @access  public
    */
    public $Path=NULL;

    /**
    * 文件上传路径的基础部分。
    * @var     string
    * @default NULL
    * @access  public
    */
    public $BasePath=NULL;

    /**
    * 文件上传路径的定位部分。
    * @var     string
    * @default NULL
    * @access  public
    */
    public $LocatePath=NULL;

    /**
    * 是否自动命名。
    * @var     bool
    * @default TRUE
    * @access  public
    */
    public $AutoNaming=TRUE;

    /**
    * 自动命名函数。
    * @var     function
    * @default NULL
    * @access  public
    */
    public $AutoNamingFunction=NULL;

    /**
    * 是否自动路径。
    * @var      bool
    * @default TRUE
    * @access  public
    */
    public $AutoPath=TRUE;

    /**
    * 自定义致命错误被触发时，是否向客户端抛HTTP状态码。
    * @var      bool
    * @default FALSE
    * @access  public
    */
    public $IsFatalErrorThrowHTTPCode=FALSE;

    /**
    * 描述：获取对外开放的标量值。
    * @param  string $name required 要获取的属性名。
    * @return mix
    */
    public function __Get($name){
        switch ($name){
            case 'Field':
                return $this->Field;
            break;
            case 'FieldCount':
                return $this->FieldCount;
            break;
            case 'UploadCount':
                return $this->UploadCount;
            break;
            case 'Filename':
                return $this->Filename;
            break;
            case 'ErrorCode':
                return $this->ErrorCode;
            break;
            case 'ErrorMessage':
                return $this->ErrorMessage;
            break;
        }
    }

    /**
    * 描述：构造函数。
    * @param  $_FIELS $fieldName 可选 文件域对象名称。
    * @return void
    */
    public function __Construct(){
        $arguments=func_get_args();
        for($i=0;$i<count($arguments);$i++){
            $fieldName=$arguments[$i];
            $situatedName=$fieldName;
            if(strpos($fieldName,'|')!==FALSE){
                $temp=explode('|',$fieldName);
                $fieldName=$temp[0];
                $situatedName=$temp[1];
                if(empty($situatedName)){
                    $situatedName='__NULL__';
                }
            }
            //--
            $this->Field[$fieldName]=array(
                'fieldName'=>$fieldName,'situatedName'=>$situatedName,
                'size'=>NULL,'extension'=>array(),'basePath'=>NULL,'locatePath'=>NULL,'path'=>NULL,
                'autoPath'=>NULL,'autoNaming'=>NULL,'autoNamingFunction'=>NULL,'object'=>array()
            );
            $this->FieldCount=$this->FieldCount+1;
        }
    }

    /**
    * 描述：新增文件域对象。
    * @access public
    * @param  string $fieldName          可选 文件域名称。
    * @param  string $size               可选 文件体积限制。
    * @param  string $extension          可选 文件类型限制。
    * @param  string $basePath           可选 基础上传路径。
    * @param  string $locatePath         可选 定位上传路径。
    * @param  string $path               可选 完整上传路径。
    * @param  string $autoPath           可选 是否自动创建路径中不存在的文件夹。
    * @param  string $autoNaming         可选 是否自动命名。
    * @param  string $autoNamingFunction 可选 自动命名函数。
    * @return void
    */
    public function AddField($fieldName,$size=NULL,$extension=NULL,$basePath=NULL,$locatePath=NULL,$path=NULL,$autoPath=NULL,$autoNaming=NULL,$autoNamingFunction=NULL){
        if($this->Type===self::UPLOADTYPE_SINGLE){
            $errorMessage=$this->FormatErrorMessage(
                'AddField','UPLOAD_ERR',self::UPLOAD_ERR,'The upload type is single,so ban called AddField method.'
            );
            if($this->IsFatalErrorThrowHTTPCode){
                header('HTTP/1.1 599 Internal Server Error!');
            }
            trigger_error($errorMessage,E_USER_ERROR);
        }
        else{
            if(($this->Type===self::UPLOADTYPE_MULTIPLEX_ALL||$this->Type===self::UPLOADTYPE_MULTIPLEX_PART)&&$this->IsFirstAddField===TRUE){
                $this->Field=array();
                $this->FieldCount=0;
            }
        }
        //--
        $situatedName=$fieldName;
        if(strpos($fieldName,'|')!==FALSE){
            $temp=explode('|',$fieldName);
            $fieldName=$temp[0];
            $situatedName=$temp[1];
            if(empty($situatedName)){
                $situatedName='__NULL__';
            }
        }
        //--
        $this->Field[$fieldName]=array(
            'fieldName'=>$fieldName,'situatedName'=>$situatedName,
            'size'=>$size,'extension'=>$extension,'basePath'=>$basePath,'locatePath'=>$locatePath,
            'path'=>$path,'autoPath'=>$autoPath,'autoNaming'=>$autoNaming,
            'autoNamingFunction'=>$autoNamingFunction
        );
        $this->FieldCount=$this->FieldCount+1;
        $this->IsFirstAddField=FALSE;
    }

    /**
    * 描述：启动上传文件规程。
    * @access public
    * @return void
    */
    public function Execute(){
        $this->InitializeBase();             //初始化上传基本质素。
        $this->InitializeError();            //初始化存储错误信息的标量。
        $this->CheckAttribute();             //检查上传设置。
        if($this->IsUploadStop_Attribute()){ //根据检查结果获取是否应该终止上传程式。
            return;
        }
        $this->CheckFields();                //检查文件域对象。
        if($this->IsUploadStop_Fields()){    //根据检查结果获取是否应该终止上传程式。
            return;
        }
        foreach($this->Field as $field){     //开始上传文件。
            $this->Upload($field);
            if($this->IsCheckFieldOnce){     //如果代模是single则只处理一个文件域对象
                break;
            }
        }
    }

    /**
    * 描述：根据上传代模初始化基本质素。
    * @access private
    * @return void
    */
    private function InitializeBase(){
        //检查Field堆是否为空，如果为空则报致命错误，终止整个PHP进程。
        if(empty($this->Field)){
            $errorMessage=$this->FormatErrorMessage(
                'Field','UPLOAD_ERR',self::UPLOAD_ERR,'The filed is empty.'
            );
            if($this->IsFatalErrorThrowHTTPCode){
                header('HTTP/1.1 598 Internal Server Error!');
            }
            trigger_error($errorMessage,E_USER_ERROR);
        }
        //检查上传代模(类型)是否属指定值，如果代模不正确将报错终止整个PHP进程。
        $uploadType=array(
            self::UPLOADTYPE_SINGLE,
            self::UPLOADTYPE_MULTI_ALL,self::UPLOADTYPE_MULTI_PART,
            self::UPLOADTYPE_MULTIPLEX_ALL,self::UPLOADTYPE_MULTIPLEX_PART
        );
        if(!in_array($this->Type,$uploadType)){
            $errorMessage=$this->FormatErrorMessage(
                'Type','UPLOAD_ERR_CUSTOM_ATTRIBUTE',self::UPLOAD_ERR_CUSTOM_ATTRIBUTE,
                'The value of Type is error,single、multi、part、multiplex_all、multiplex_part.'
            );
            if($this->IsFatalErrorThrowHTTPCode){
                header('HTTP/1.1 597 Internal Server Error!');
            }
            trigger_error($errorMessage,E_USER_ERROR);
        }
        //依据上传代模设置是否"只检查1次上传属性"的布尔值。
        if($this->Type===self::UPLOADTYPE_SINGLE||$this->Type===self::UPLOADTYPE_MULTI_ALL||
           $this->Type===self::UPLOADTYPE_MULTI_PART){
            $this->IsCheckAttributeOnce=TRUE;
        }
        //依据上传代模设置"在检查上传属性发生错误"时是否终止程式。
        if($this->Type===self::UPLOADTYPE_SINGLE||$this->Type===self::UPLOADTYPE_MULTI_ALL||
           $this->Type===self::UPLOADTYPE_MULTI_PART){
            $this->IsCheckAttributeErrorStop=TRUE;
        }
        //依据上传代模设置是否"只检查一个"Field对象。
        if($this->Type===self::UPLOADTYPE_SINGLE){
            $this->IsCheckFieldOnce=TRUE;
        }
        //依据上传代模设置"检查Field对象出错"时是否终止程式。
        if($this->Type===self::UPLOADTYPE_SINGLE){
            $this->IsCheckFieldErrorStop=TRUE;
        }
    }

    /**
    * 描述：根据上传代模初始化存储错误信息的标量。
    * @access private
    * @return void
    */
    private function InitializeError() {
        if($this->Type===self::UPLOADTYPE_SINGLE){
            $this->ErrorCode=NULL;
            $this->ErrorMessage=NULL;
        }
        elseif($this->Type===self::UPLOADTYPE_MULTI_PART||$this->Type===self::UPLOADTYPE_MULTI_ALL){
            $this->ErrorCode=array('default'=>NULL,'field'=>array());
            $this->ErrorMessage=array('default'=>NULL,'field'=>array());
        }
        elseif($this->Type===self::UPLOADTYPE_MULTIPLEX_ALL||$this->Type===self::UPLOADTYPE_MULTIPLEX_PART){
            $this->ErrorCode=array();
            $this->ErrorMessage=array();
        }
    }

    /**
    * 描述：检查上传设置是否符合运行时规格。
    * @access private
    * @return void
    */
    private function CheckAttribute(){
        foreach($this->Field as $fieldName=>&$field){
            //建构field对象。
            $this->CheckAttribute_MakeField($field);
            //检查上传指定的文件体积。
            $isWrong=$this->CheckAttribute_Size($field);
            if($isWrong){
                if($this->IsCheckAttributeErrorStop){
                    break;
                }
                else{
                    continue;
                }
            }
            //检查上传文件扩展名。
            $isWrong=$this->CheckAttribute_Extension($field);
            if($isWrong){
                if($this->IsCheckAttributeErrorStop){
                    break;
                }
                else{
                    continue;
                }
            }
            //检查上传文件路径。
            $isWrong=$this->CheckAttribute_Path($field);
            if($isWrong){
                if($this->IsCheckAttributeErrorStop){
                    break;
                }
                else{
                    continue;
                }
            }
            //检查自动创建路径。
            $isWrong=$this->CheckAttribute_AutoPath($field);
            if($isWrong){
                if($this->IsCheckAttributeErrorStop){
                    break;
                }
                else{
                    continue;
                }
            }
            //检查自动命名函数。
            $isWrong=$this->CheckAttribute_AutoNaming($field);
            if($isWrong){
                if($this->IsCheckAttributeErrorStop){
                    break;
                }
                else{
                    continue;
                }
            }
            //如果代模是single只检查1次并终止检查程式
            if($this->IsCheckFieldOnce===TRUE){
                break;
            }
        }
    }

    /**
    * 描述：构造虚拟文件域对象。
    * @access private
    * @param  array   $field 必选 虚拟文件域对象。
    * @return void
    */
    private function CheckAttribute_MakeField(&$field){
        if($this->IsCheckAttributeOnce){
            $this->CheckAttribute_MakeField_Unitary($field);
        }
        else{
            $this->CheckAttribute_MakeField_Multiplex($field);
        }
    }

    /**
    * 描述：单元代模下构造虚拟文件域对象。
    * @access private
    * @param  array   $field 必选 虚拟文件域对象。
    * @return void
    */
    private function CheckAttribute_MakeField_Unitary(&$field){
        $field['size']=$this->Size;
        $field['extension']=$this->Extension;
        $field['path']=$this->Path;
        $field['basePath']=$this->BasePath;
        $field['locatePath']=$this->LocatePath;
        $field['autoPath']=$this->AutoPath;
        $field['autoNaming']=$this->AutoNaming;
        $field['autoNamingFunction']=$this->AutoNamingFunction;
    }

    /**
    * 描述：多元代模下构造虚拟文件域对象。
    * @access private
    * @param  array   $field 必选 虚拟文件域对象。
    * @return void
    */
    private function CheckAttribute_MakeField_Multiplex(&$field){
        if(!array_key_exists('size',$field)||is_null($field['size'])||empty($field['size'])){
            $field['size']=$this->Size;
        }
        //--
        if(!array_key_exists('extension',$field)||is_null($field['extension'])||empty($field['extension'])){
            $field['extension']=$this->Extension;
        }
        //--
        if(!array_key_exists('path',$field)||is_null($field['path'])||empty($field['path'])){
            $field['path']=$this->Path;
        }
        if(!array_key_exists('basePath',$field)||is_null($field['basePath'])||empty($field['basePath'])){
            $field['basePath']=$this->BasePath;
        }
        if(!array_key_exists('locatePath',$field)||is_null($field['locatePath'])||empty($field['locatePath'])){
            $field['locatePath']=$this->LocatePath;
        }
        //--
        if(!array_key_exists('autoPath',$field)||is_null($field['autoPath'])||empty($field['autoPath'])){
            $field['autoPath']=$this->AutoPath;
        }
        //--
        if(!array_key_exists('autoNaming',$field)||is_null($field['autoNaming'])||empty($field['autoNaming'])){
            $field['autoNaming']=$this->AutoNaming;
        }
        //--
        if(!array_key_exists('autoNamingFunction',$field)||is_null($field['autoNamingFunction'])||empty($field['autoNamingFunction'])){
            $field['autoNamingFunction']=$this->AutoNamingFunction;
        }
    }

    /**
    * 描述：检查上传设置之文件体积。
    * @access private
    * @param  array   $field 必选 虚拟文件域对象。
    * @return bool
    */
    private function CheckAttribute_Size(&$field){
        if(!is_numeric($field['size'])){
            $errorMessage=$this->FormatErrorMessage(
                'Attribute::Size','UPLOAD_ERR_CUSTOM_ATTRIBUTE',self::UPLOAD_ERR_CUSTOM_ATTRIBUTE,
                'Size value is not numeric.'
            );
            $this->SetError(self::UPLOAD_ERR_CUSTOM_ATTRIBUTE,$errorMessage,'default',$field['fieldName']);
            return TRUE;
        }
        return FALSE;
    }

    /**
    * 描述：检查上传设置之文件类型。
    * @access private
    * @param  array   $field 必选 虚拟文件域对象。
    * @return bool
    */
    private function CheckAttribute_Extension(&$field){
        $isNotExtension=is_array($field['extension'])===FALSE;
        $isNotExtension=$isNotExtension||(array_keys($field['extension'])!==range(0,count($field['extension'])-1)); //如果不是关联数组。
        if($isNotExtension){
            $errorMessage=$this->FormatErrorMessage(
                'Attribute::Extension','UPLOAD_ERR_CUSTOM_ATTRIBUTE',self::UPLOAD_ERR_CUSTOM_ATTRIBUTE,
                "Extension value type must be digital array,for example:array('gif','png')."
            );
            $this->SetError(self::UPLOAD_ERR_CUSTOM_ATTRIBUTE,$errorMessage,'default',$field['fieldName']);
            return TRUE;
        }
        return FALSE;
    }

    /**
    * 描述：检查上传设置之上传路径。
    * @access private
    * @param  array   $field 必选 虚拟文件域对象。
    * @return bool
    */
    private function CheckAttribute_Path(&$field){
        if(!isset($field['path'])&&empty($field['path'])){
            if(is_null($field['basePath'])||is_null($field['basePath'])){
                $errorMessage=$this->FormatErrorMessage(
                    'Attribute::Path','UPLOAD_ERR_CUSTOM_ATTRIBUTE',self::UPLOAD_ERR_CUSTOM_ATTRIBUTE,
                    'Upload path value is NULL or empty.'
                );
                $this->SetError(self::UPLOAD_ERR_CUSTOM_ATTRIBUTE,$errorMessage,'default',$field['fieldName']);
                return TRUE;
            }
        }
        return FALSE;
    }

    /**
    * 描述：检查上传设置之上传路径存在。
    * @access private
    * @param  array   $field 必选 虚拟文件域对象。
    * @return bool
    */
    private function CheckAttribute_AutoPath(&$field){
        $path=$this->MakeUploadPath($field);
        if(!is_dir($path)){
            if($field['autoPath']){
                $this->CreatePath($path); //如果上传路径不存在，而且自动创建路径为TRUE则创建该路径，否则报运行时错误。
            }
            else{
                $errorMessage=$this->FormatErrorMessage(
                    'Attribute::AutoPath','UPLOAD_ERR_CUSTOM_ATTRIBUTE',self::UPLOAD_ERR_CUSTOM_ATTRIBUTE,
                    sprintf('Upload path not exist,Meanwhile autoPath attribute value is FALSE,the path is %s.',$path)
                );
                $this->SetError(self::UPLOAD_ERR_CUSTOM_ATTRIBUTE,$errorMessage,'default',$field['fieldName']);
            }
            return TRUE;
        }
        return FALSE;
    }

    /**
    * 描述：检查上传设置之自动命名函数。
    * @access private
    * @param  array   $field 必选 虚拟文件域对象。
    * @return bool
    */
    private function CheckAttribute_AutoNaming(&$field){
        if(isset($field['autoNamingFunction'])){
            if('object'!==gettype($field['autoNamingFunction'])){ //检测出并不是函数类型。
                $errorMessage=$this->FormatErrorMessage(
                    'Attribute::AutoNamingFunction','UPLOAD_ERR_CUSTOM_ATTRIBUTE',self::UPLOAD_ERR_CUSTOM_ATTRIBUTE,
                    'AutoNamingFunction value is not Funtion.'
                );
                $this->SetError(self::UPLOAD_ERR_CUSTOM_ATTRIBUTE,$errorMessage,TRUE,'default',$field['fieldName']);
                return TRUE;
            }
        }
        return FALSE;
    }

    /**
    * 描述：检查完所有上传设置后，根据不同的上传代模，返回是否需要停止上传规程的判定。
    * @access private
    * @return bool
    */
    private function IsUploadStop_Attribute(){
        if($this->Type===self::UPLOADTYPE_SINGLE){
            if($this->ErrorCode===NULL){
                return FALSE;
            }
            else{
                return TRUE;
            }
        }
        elseif($this->Type===self::UPLOADTYPE_MULTI_PART||$this->Type===self::UPLOADTYPE_MULTI_ALL){
            if(empty($this->ErrorCode['default'])){
                return FALSE;
            }
            else{
                return TRUE;
            }
        }
        elseif($this->Type===self::UPLOADTYPE_MULTIPLEX_PART){
            //@就算是上传设置错误也不要紧，因为多元异质合格上传每个上传文件有自己的设置，无需理会。
            return FALSE;
        }
        elseif($this->Type===self::UPLOADTYPE_MULTIPLEX_ALL){
            if(empty($this->ErrorCode)){
                return FALSE;
            }
            else{
                //多元异质也要求一点错误没有才能上传，如果发现一个错误则停止。
                //此时还没有执行checkFields方法，所以field对象的errorCode如果没有就应该是空的。
                //如果不是空的证明最少有1个field对象的上传属性设置出错，所以停止上传。
                return TRUE;
            }
        }
    }

    /**
    * 描述：检查文件域对象。
    * @access private
    * @return bool
    */
    private function CheckFields(){
        foreach($this->Field as &$field){
            $result=$this->CheckFields_Do($field);
            if($result==='break'){
                return;     //返回break是检查程式根据上传代模检查的结果。
            }
            else{
                if($this->IsCheckFieldOnce){
                    return; //一元上传时只检查1个field对象，所以若上面没错到这里也终止检查程式。
                }
                continue;
            }
        }
    }

    /**
    * 描述：检查具体文件域的对象。
    * @access private
    * @return bool
    */
    private function CheckFields_Do(&$field){
        if(isset($_FILES[$field['fieldName']])){                      //根据是否存在Field对象而处理。
            $field['object']=$_FILES[$field['fieldName']];
            if($field['object']['error']===self::UPLOAD_ERR_OK){      //检测$FIELD标量的PHP引擎错误。
                $extName=$this->GetExtensionName($field['object']['name']); //检测文件域扩展名正确性。
                if(!in_array($extName,$field['extension'])){
                    $this->CheckFields_Error_Extension($field,$extName);
                    return $this->CheckFieldErrorStopValue_CheckFields();
                }
                else{
                    if($field['size']<$field['object']['size']){      //检测文件域体积正确性。
                        $this->CheckFields_Error_Size($field);
                        return $this->CheckFieldErrorStopValue_CheckFields();
                    }
                    else{
                        $this->CheckFields_Ok($field);
                        return 'continue';
                    }
                }
            }
            else{
                $this->CheckFields_Error_Field($field);
                return $this->CheckFieldErrorStopValue_CheckFields();
            }
        }
        else{
            $this->CheckFields_Error_NoField($field);
            return $this->CheckFieldErrorStopValue_CheckFields();
        }
    }

    /**
    * 描述：检测文件域发现错误时不直接使用IsCheckFieldErrorStop属性值，而是将该值转为其它涵意英文词汇。
    * @access private
    * @param  array   $field 必选 虚拟的文件域对像。
    * @return void
    */
    private function CheckFieldErrorStopValue_CheckFields(){
        return $this->IsCheckFieldErrorStop ? 'break':'continue';
    }

    /**
    * 描述：设置运行时错误之没有上传文件。
    * @access private
    * @param  array   $field 必选 虚拟的文件域对像。
    * @return void
    */
    private function CheckFields_Error_NoField(&$field){
        $errorMessage=$this->FormatErrorMessage(
            'CheckFields::Error_NoField','UPLOAD_ERR_CUSTOM_NOFIELD',self::UPLOAD_ERR_CUSTOM_NOFIELD,
            sprintf('File field not exist,the name is %s.',$field['fieldName'])
        );
        $this->SetError(self::UPLOAD_ERR_CUSTOM_NOFIELD,$errorMessage,'field',$field['fieldName']);
    }

    /**
    * 描述：设置运行时错误之文件域发生PHP错误。
    * @access private
    * @param  array   $field 必选 虚拟的文件域对像。
    * @return void
    */
    private function CheckFields_Error_Field(&$field){
        $PHPUploadErrorName=array(
            UPLOAD_ERR_OK=>'UPLOAD_ERR_OK',                 //文件上传成功。@
            UPLOAD_ERR_INI_SIZE=>'UPLOAD_ERR_INI_SIZE',     //上传文件大小超出了php.ini中upload_max_filesize的值。
            UPLOAD_ERR_FORM_SIZE=>'UPLOAD_ERR_FORM_SIZE',   //上传文件大小超出了表单中MAX_FILE_SIZE的值。
            UPLOAD_ERR_PARTIAL=>'UPLOAD_ERR_PARTIAL',       //文件只上传了一部分，没有被全部上传。
            UPLOAD_ERR_NO_FILE=>'UPLOAD_ERR_NO_FILE',       //文件域中没有文件。
            UPLOAD_ERR_NO_TMP_DIR=>'UPLOAD_ERR_NO_TMP_DIR', //找不到临时目录。
            UPLOAD_ERR_CANT_WRITE=>'UPLOAD_ERR_CANT_WRITE', //写入失败，可能没有权限。
            UPLOAD_ERR_EXTENSION=>'UPLOAD_ERR_EXTENSION'    //文件上传过程中被停止。
        );
        //--
        $temp=$PHPUploadErrorName[$field['object']['error']];
        $errorMessage=$this->FormatErrorMessage(
            'CheckFields::Error_Field',$temp,$field['object']['error'],
            sprintf('Field error is PHP engine error,name is %s.',$temp)
        );
        $this->SetError($field['object']['error'],$errorMessage,'field',$field['fieldName']);
    }

    /**
    * 描述：设置运行时错误之文件类型错误。
    * @access private
    * @param  array   $field         必选 虚拟的文件域对像。
    * @param  array   $extensionName 必选 当前文件域文件类型扩展名。
    * @return void
    */
    private function CheckFields_Error_Extension(&$field,$extensionName){
        $errorMessage=$this->FormatErrorMessage(
            'CheckFields::Extension',
            'UPLOAD_ERR_CUSTOM_EXTENSION',
            self::UPLOAD_ERR_CUSTOM_EXTENSION,
            sprintf('File extension is error,option extension is %s,upload file extension is %s.',implode(' or ',$field['extension']),$extensionName)
        );
        $this->SetError(self::UPLOAD_ERR_CUSTOM_EXTENSION,$errorMessage,'field',$field['fieldName']);
    }

    /**
    * 描述：设置运行时错误之文件体积。
    * @access private
    * @param  array   $field 必选 虚拟的文件域对像。
    * @return void
    */
    private function CheckFields_Error_Size(&$field){
        $errorMessage=$this->FormatErrorMessage(
            'CheckFields::Size',
            'UPLOAD_ERR_CUSTOM_SIZE',
            self::UPLOAD_ERR_CUSTOM_SIZE,
            sprintf('Filesize greater than option size. option size is %s.',$field['size'])
        );
        $this->SetError(self::UPLOAD_ERR_CUSTOM_SIZE,$errorMessage,'field',$field['fieldName']);
    }

    /**
    * 描述：设置运行时错误之文件域正确。
    * @access private
    * @param  array   $field 必选 虚拟的文件域对像。
    * @return void
    */
    private function CheckFields_Ok(&$field){
        $this->SetError(self::UPLOAD_ERR_OK,'','field',$field['fieldName']); //没错也标出是UPLOAD_ERR_OK。
    }

    /**
    * 描述：检查完所有文件域对象后，根据不同的上传代模，返回是否需要终止上传规程的判定。
    * @access private
    * @return bool
    */
    private function IsUploadStop_Fields(){
        if($this->Type===self::UPLOADTYPE_SINGLE){
            if($this->ErrorCode===self::UPLOAD_ERR_OK){
                return FALSE;
            }
            else{
                return TRUE;
            }
        }
        elseif($this->Type===self::UPLOADTYPE_MULTI_PART){
            $errorCount=0;
            foreach($this->ErrorCode['field'] as $fieldName=>$code){
                if($code!==self::UPLOAD_ERR_OK){
                    $errorCount++;
                }
            }
            if(count($this->Field)===$errorCount){ //在part模式下所有field出错应该停止。
                return TRUE;
            }
            return FALSE;
        }
        elseif($this->Type===self::UPLOADTYPE_MULTI_ALL){
            foreach($this->Field as $field){
                if($this->ErrorCode['field'][$field['fieldName']]===self::UPLOAD_ERR_OK){
                    continue;
                }
                else{
                    return TRUE; //因为全部要求合格，所以出一点错就要终止上传。
                }
            }
            return FALSE;
        }
        elseif($this->Type===self::UPLOADTYPE_MULTIPLEX_PART){
            $errorCount=0;
            foreach($this->ErrorCode as $fieldName=>$code){
                if($code!==self::UPLOAD_ERR_OK){
                    $errorCount++;
                }
            }
            if(count($this->Field)===$errorCount){ //在part模式下所有field出错应该停止。
                return TRUE;
            }
            return FALSE;
        }
        elseif($this->Type===self::UPLOADTYPE_MULTIPLEX_ALL){
            foreach($this->Field as $field){
                if($this->ErrorCode[$field['fieldName']]===self::UPLOAD_ERR_OK){
                    continue;
                }
                else{
                    return TRUE; //因为全部要求合格，所以出一点错就要终止上传。
                }
            }
            return FALSE;
        }
    }

    /**
    * 描述：开始上传文件。
    * @access private
    * @param  array   $field 必选 虚拟的文件域对像。
    * @return void
    */
    private function Upload(&$field){
        if($this->Upload_isOkField($field)===FALSE){ //如果代模是part类型，一定会有不合格的field对象，这样的对象不能上传。
            return;                                  //如果代模是其它类型，根本过不到这，会在这之前就终止的。
        }
        $result=$this->Upload_Autoname($field);      //处理自动命名。
        if($result===NULL){
            return;
        }
        $this->Upload_Move($field,$result);          //移动临时到指定路径。
    }

    /**
    * 描述：检测文件域是否正确，可用于上传程式。
    * @access private
    * @param  array   $field 必选 虚拟的文件域对像。
    * @return void
    */
    private function Upload_isOkField(&$field){
        //只会在part代模下检查，因为只有part代模才会出不合格的field，其它模式必须全部合格。
        if($this->Type===Uploader::UPLOADTYPE_MULTI_PART){
            if($this->ErrorCode['field'][$field['fieldName']]===Uploader::UPLOAD_ERR_OK){
                return TRUE;
            }
            else{
                return FALSE;
            }
        }
        elseif($this->Type===Uploader::UPLOADTYPE_MULTIPLEX_PART){
            if($this->ErrorCode[$field['fieldName']]===Uploader::UPLOAD_ERR_OK){
                return TRUE;
            }
            else{
                return FALSE;
            }
        }
    }

    /**
    * 描述：处理自动命名程式。
    * @access private
    * @param  array   $field 必选 虚拟的文件域对像。
    * @return void
    */
    private function Upload_Autoname(&$field){
        $filename=$field['object']['name'];
        $extName=$this->GetExtensionName($filename);
        //--
        if($field['autoNaming']){
            if($field['autoNamingFunction']){
                $filename=$field['autoNamingFunction']($filename); //用户自定义函数生成。
            }
            else{
                $filename=date('Ymd').time().rand(1000,2000);      //默认生成。
            }
            //检测文件名是为空。
            if(empty($filename)||is_null($filename)){
                $errorMessage=$this->FormatErrorMessage('Upload','UPLOAD_ERR_CUSTOM_FILENAME',self::UPLOAD_ERR_CUSTOM_FILENAME,
                    sprintf('Create the file name is nonlicet,filename is empty.',$filename)
                );
                $this->SetError(self::UPLOAD_ERR_CUSTOM_FILENAME,$errorMessage,'field',$field['fieldName']);
                return NULL;
            }
            $filename=$filename.'.'.$extName;
            //检测文名长度。
            if(strlen($filename)>127){
                $errorMessage=$this->FormatErrorMessage('Upload','UPLOAD_ERR_CUSTOM_FILENAME',self::UPLOAD_ERR_CUSTOM_FILENAME,
                    sprintf('Create the file name is nonlicet,filename is too long,length is 127 bytes.',$filename)
                );
                $this->SetError(self::UPLOAD_ERR_CUSTOM_FILENAME,$errorMessage,'field',$field['fieldName']);
                return NULL;
            }
        }
        //--
        return array('path'=>$this->MakeUploadPath($field),'filename'=>$filename);
    }

    /**
    * 描述：移动临时文件到上传路径。
    * @access private
    * @param  array   $field 必选 虚拟的文件域对像。
    * @param  array   $data  必选 移动所用必需数据，上传路径和文件名。
    * @return void
    */
    private function Upload_Move(&$field,$data){
        $tmpName=$field['object']['tmp_name'];
        $newFilename=str_replace('//','/',$data['path']).'/'.$data['filename'];
        $isUpload=@move_uploaded_file($tmpName,$newFilename);
        if($isUpload){
            if($this->Type===self::UPLOADTYPE_SINGLE){
                $this->Filename=$data['filename'];
            }
            else{
                $this->Filename[$field['fieldName']]=$data['filename'];
            }
            $this->UploadCount+=1;
        }
        else{
            $errorMessage=$this->FormatErrorMessage('Upload','UPLOAD_ERR_CUSTOM_MOVE',self::UPLOAD_ERR_CUSTOM_MOVE,'Move file failed.');
            $this->SetError(self::UPLOAD_ERR_CUSTOM_MOVE,$errorMessage,'field',$field['fieldName']);
        }
    }

    /**
    * 描述：根据上传设置返回完整的上传路径。
    * @access private
    * @param  array   $field 必选 文件域对象。
    * @return void
    */
    private function MakeUploadPath(&$field){
        $path=$field['path'];
        if(is_null($path)){
            $path=$field['basePath'].'/'.$field['locatePath'];
            $path=str_replace('//','/', $path);
            $path=str_replace('/\\','/', $path);
        }
        return $path;
    }

    /**
    * 描述：设置错误运行时错误。
    * @param int    $code    必选 错误代码值。
    * @param string $message 必选 错误信息说明。
    * @param string $type    可选 错误类型，是上传设置错误或文件域对象错误。
    * @param string $meta    可选 错误的辅助信息，文件域对象名称。
    * @return void
    */
    private function SetError($code,$message,$type=NULL,$meta=NULL){
        if($this->Type===self::UPLOADTYPE_SINGLE){
            $this->ErrorCode=$code;
            $this->ErrorMessage=$message;
        }
        elseif($this->Type===self::UPLOADTYPE_MULTIPLEX_ALL
             ||$this->Type===self::UPLOADTYPE_MULTIPLEX_PART){
            $this->ErrorCode[$meta]=$code;
            $this->ErrorMessage[$meta]=$message;
        }
        else{
            if($type==='field'){
                $this->ErrorCode['field'][$meta]=$code;
                $this->ErrorMessage['field'][$meta]=$message;
            }
            else{
                $this->ErrorCode['default']=$code;
                $this->ErrorMessage['default']=$message;
            }
        }
        #error_log($errorMessge); //感觉全是由客户端可以主动制造的没必要写日志。
    }

    /**
    * 描述：根据传递过来的错误信息参数生成统一格式的错误信息。
    * @param  string $source   必选 错误来源说明。
    * @param  string $literals 必选 错误码字面值。
    * @param  int    $code     必选 错误码值。
    * @param  string $message  必选 错误信息说明。
    * @return string
    */
    private function FormatErrorMessage($source,$literals,$code,$message){
        return sprintf('TryLibaray::Uploader::%s,literals is `%s`,error code is `%s`,error message is `%s`',
            $source,$literals,$code,$message
        );
    }

    /**
    * 描述：获取文件扩展名。
    * @access private
    * @param  string  $filename 必选 文件原始名称。
    * @return string
    */
    private function GetExtensionName($filename){
        $tempArray=explode('.', $filename);
        $value=strtolower($tempArray[count($tempArray)-1]);
        return $value;
    }

    /**
    * 描述：自动创建文件上传路径。
    * @access private
    * @param  string  $path 必选 上传路径，也就是Path属性的值。
    * @return void
    */
    private function CreatePath($path){
        if(!is_dir($path)){
            $path=explode('/',str_replace('\\','/',$path));
            for($i=0;$i<count($path);$i++){
                $temp=join('/',array_slice($path,0,$i+1));
                if(!empty($temp)&&!is_dir($temp)){
                    mkdir($temp);
                }
            }
        }
    }

    /**
    * 描述：返回上传过程报告，包括所有重要属性和结果值。
    * @access public
    * @param  string $urlPath 必选 通过url访问图片的路径。
    * @return string
    */
    public function PrintDebugInfo($urlPath=NULL){
        $result='';
        if($this->Type===self::UPLOADTYPE_SINGLE){
            $result.=sprintf('ErrorCode:%s<br/>',$this->ErrorCode);
            $result.=sprintf('ErrorMessage:%s<br/>',$this->ErrorMessage);
        }
        elseif($this->Type===self::UPLOADTYPE_MULTI_PART||$this->Type===self::UPLOADTYPE_MULTI_ALL){
            if($this->ErrorCode['default']){
                $result.=sprintf('ErrorCode:%s<br/>',$this->ErrorCode['default']);
                $result.=sprintf('ErrorMessage:%s<br/>',$this->ErrorMessage['default']);
            }
            else{
                $result.='<b>ErrorCode</b><br/>';
                foreach($this->ErrorCode['field'] as $fieldName=>$code){
                    $result.=sprintf('%s:%s<br/>',$fieldName,$code);
                }
                $result.='<b>ErrorMessage</b><br/>';
                foreach($this->ErrorMessage['field'] as $fieldName=>$message){
                    $result.=sprintf('%s:%s<br/>',$fieldName,$message);
                }
            }
        }
        elseif($this->Type===self::UPLOADTYPE_MULTIPLEX_PART||$this->Type===self::UPLOADTYPE_MULTIPLEX_ALL){
            $result.='<b>ErrorCode</b><br/>';
            foreach($this->ErrorCode as $fieldName=>$code){
                $result.=sprintf('%s:%s<br/>',$fieldName,$code);
            }
            $result.='<b>ErrorMessage</b><br/>';
            foreach($this->ErrorMessage as $fieldName=>$message){
                $result.=sprintf('%s:%s<br/>',$fieldName,$message);
            }
        }
        $result.=sprintf('<hr/>');
        $result.=sprintf('Type:%s<br/>',$this->Type);
        $result.=sprintf('Size:%s<br/>',$this->Size);
        $result.=sprintf('Extension:%s<br/>',gettype($this->Extension)==='array'?join('|',$this->Extension):$this->Extension);
        $result.=sprintf('Path:%s<br/>',$this->Path);
        $result.=sprintf('BasePath:%s<br/>',$this->BasePath);
        $result.=sprintf('LocatePath:%s<br/>',$this->LocatePath);
        $result.=sprintf('AutoPath:%s<br/>',$this->AutoPath);
        $result.=sprintf('AutoNaming:%s<br/>',$this->AutoNaming);
        $result.=sprintf('AutoNamingFunction:%s<br/>',$this->AutoNamingFunction);
        $result.=sprintf('<hr/>');
        $result.=sprintf('IsCheckAttributeOnce:%s<br/>',$this->IsCheckAttributeOnce);
        $result.=sprintf('IsCheckAttributeErrorStop:%s<br/>',$this->IsCheckAttributeErrorStop);
        $result.=sprintf('IsCheckFieldOnce:%s<br/>',$this->IsCheckFieldOnce);
        $result.=sprintf('IsCheckFieldErrorStop:%s<br/>',$this->IsCheckFieldErrorStop);
        $result.=sprintf('IsFatalErrorThrowHTTPCode:%s<br/>',$this->IsFatalErrorThrowHTTPCode);
        $result.=sprintf('<hr/>');
        foreach($this->Field as $fieldName=>$field){
            $result.=sprintf('<b>%s</b><br/>',$fieldName);
            $result.=sprintf('size:%s<br/>',$field['size']);
            $result.=sprintf('ext:%s<br/>',gettype($field['extension'])==='array'?join('|',$field['extension']):$field['extension']);
            $result.=sprintf('path:%s<br/>',$field['path']);
            $result.=sprintf('basePath:%s<br/>',$field['basePath']);
            $result.=sprintf('locatePath:%s<br/>',$field['locatePath']);
            $result.=sprintf('autoPath:%s<br/>',$field['autoPath']);
            $result.=sprintf('autoNaming:%s<br/>',$field['autoNaming']);
            $result.=sprintf('autoNamingFunction:%s<br/>',$field['autoNamingFunction']);
            if(isset($field['object'])){
                $temp=NULL;
                foreach($field['object'] as $key=>$value){
                    $temp.=sprintf('%s(%s)|',$key,$value);
                }
                $result.=sprintf('object:%s<br/>',$temp);
            }
        }
        $result.=sprintf('<hr/>');
        $result.=sprintf('FieldCount:%s<br/>',$this->FieldCount);
        $result.=sprintf('UploadCount:%s<br/>',$this->UploadCount);
        $result.=sprintf('<hr/>');
        //--
        $temp=NULL;
        if(gettype($this->Filename)==='array'){
            foreach($this->Filename as $fieldName=>$fileName){
                if(is_null($urlPath)){
                    $urlPath=$this->GetUrlPath($this->Field[$fieldName]);
                }
                //--
                if(is_null($urlPath)){
                    $value=sprintf('%s:%s(无法预览)<br/>',$fieldName,$fileName);
                }
                else{
                    $value=sprintf('%s:<a href="%s" target="_blank">%s</a><br/>',$fieldName,$urlPath.'/'.$fileName,$fileName);
                }
                $temp.=$value;
            }
            $temp=substr($temp,0,strlen($temp)-5);
            $temp='<br/>'.$temp;
        }
        else{
            if(is_null($urlPath)){
                $x=$this->Field;
                $urlPath=$this->GetUrlPath(current($x));
            }
            //--
            if(is_null($urlPath)){
                $temp=sprintf('%s(无法预览)',$this->Filename);
            }
            else{
                $temp=sprintf('<a href="%s" target="_blank">%s</a>',$urlPath.'/'.$this->Filename,$this->Filename);
            }
        }
        $result.=sprintf('Filename:%s<br/>',$temp);
        return $result;
    }

    /**
    * 描述：获取预览文件的定位路径。
    * @access private
    * @param  array       $field   必选 虚拟文件域对象。
    * @return NULL|string
    */
    private function GetUrlPath($field){
        if(is_null($field['path'])){
            if(is_null($field['basePath'])||is_null($field['locatePath'])){
                $path=NULL;
            }
            else{
                $path=$field['locatePath'];
            }
        }
        else{
            $path=NULL;
        }
        return $path;
    }

    /**
    * 描述：返回以适合单位计算的字节值。
    * @static
    * @access public
    * @param  number $bytesize 必选 字节数。
    * @return string
    */
    public static function SizeFormat($bytesize){
        $i=0;
        //当$bytesize 大于是1024字节时，开始循环，当循环到第4次时跳出；
        while(abs($bytesize)>=1024){
            $bytesize=$bytesize/1024;
            $i++;
            if($i==4){
                break;
            }
        }
        //将Bytes,KB,MB,GB,TB定义成一维数组；
        $units= array("bytes","kb","mb","gb","tb");
        $newsize=round($bytesize,2);
        return("$newsize $units[$i]");
    }
}